<template>
    <div>
        <treeItem ref="child" 
            :nodeKey="nodeKey" 
            :labelKey="labelKey" 
            :selectId="selectId" 
            :showAll="showAll" 
            :item="item" 
            :level="0" 
            v-for="(item, index) in data" 
            :key="index">
        </treeItem>
    </div>
</template>

<script>
import treeItem from './treeItem.vue'

export default {
    props: {
        // 数据源
        data: {
            default: function () {
                return []
            },
            type: Array
        },
        // 显示 用 Key 值
        labelKey: {
            default: 'title',
            type: String
        },
        // 是否全部展开
        showAll: {
            default: false,
            type: Boolean
        },
        // 数据的唯一标识
        nodeKey: {
            default: 'id',
            type: String
        },
        selectId: {
            default: null
        }
    },
    components: {
        treeItem
    },
    data() {
        return {
            treeNode: [],
            whileCache: []
        }
    },
    // 另一种跨组件传值方法 （provide）
    provide() {
        return {
            setTreeNode: (item) => {
                // 将渲染结束的所有子集存放在 节点数组中
                this.treeNode.push(item)
            },
            // 触发缓存对象，由于开始渲染时，节点尚未生成，不能以ID为关键字进行节点操作
            // 故进行缓存，在数据渲染结束之后，再将实际节点值插入到树形显示结构中
            setChildchecked: (id) => {
                this.whileCache.push(id)
            },
            // 数据刷新 方法
            refreshEvent: (changeNode, val) => {
                // 以 唯一值 为条件，将树形数据转换为字典格式
                var mapping = {}
                for (let i = 0; i < this.treeNode.length; i++) {
                    const element = this.treeNode[i]
                    mapping[element.item[this.nodeKey]] = element
                }
                // 为子集的选中状态赋值
                changeNode.checked = val
                // 需要手动取消半选样式
                changeNode.indeterminate = false

                // 递归方法，判断父级是否选中
                var setParentValue = (node) => {
                    // 没有父级，跳过
                    if (!node.parentKey) return
                    // 从集合中选中 父级的对象（ 以字典项 方式查找，提高查询效率）
                    var parentNode = mapping[node.parentKey]
                    // 渲染结束前处理选中状态，会出现值传递获取失败问题，停止处理
                    if (!parentNode.item) return
                    // 变量缓存，子集是否都选中了
                    var isAllCheck = true
                    // 变量缓存，子集是否都没有选中
                    var isAllUnCheck = true
                    // 变量缓存，是否为半选状态
                    var hasIndeterminate = false
                    // 遍历全部子集，为变量缓存的数据赋值
                    for (let i = 0; i < parentNode.item.children.length; i++) {
                        const element = parentNode.item.children[i]
                        var child = mapping[element[this.nodeKey]]
                        // 有选中的，则 子集都没有选中 的缓存为 否
                        if (child.checked) {
                            isAllUnCheck = false
                        }
                        // 有没选中的，则 子集全部选中 的缓存为 否
                        if (!child.checked) {
                            isAllCheck = false
                        }
                        // 有半选状态，则 半选状态 的缓存为 真
                        if (child.indeterminate) {
                            hasIndeterminate = true
                        }
                    }
                    // 根据缓存状态，刷新当前节点状态样式
                    if (isAllCheck) {
                        parentNode.indeterminate = false
                        parentNode.checked = true
                    }
                    if (isAllUnCheck) {
                        parentNode.indeterminate = false
                        parentNode.checked = false
                    }
                    if ((!isAllCheck && !isAllUnCheck) || hasIndeterminate) {
                        parentNode.checked = false
                        parentNode.indeterminate = true
                    }
                    // 如果有父级，进行递归，向上判断
                    if (parentNode.parentKey) {
                        setParentValue(parentNode)
                    }
                }

                // 子集状态递归方法
                var setChildrenValue = (node, value) => {
                    // 直接为子集状态赋值
                    if (node.item.children && node.item.children.length) {
                        node.item.children.forEach((ele) => {
                            var childNode = mapping[ele[this.nodeKey]]
                            childNode.checked = value
                            setChildrenValue(childNode, value)
                        })
                    }
                }
                this.$nextTick(() => {
                    // 刷新子集状态
                    setChildrenValue(changeNode, val)
                    // 刷新父级状态
                    setParentValue(changeNode)

                    this.$nextTick(() => {
                        // 手动触发 change 方法，并且为事件传递值 （树节点数据，Vue节点，选中状态）
                        this.$emit('change', changeNode.item, changeNode, val)
                    })
                })
            },
            clickEvent: (item) => {
                // 手动触发 节点点击事件
                this.$emit('nodeClick', item);
            }
        }
    },
    mounted() {
        this.$nextTick(() => {
            this.setCheckValueById(this.whileCache, true)
        })
    },
    methods: {
        // 根据ID 获取节点
        getNodeById(id) {
            var _node = null
            // 由于动态添加节点问题，故不能存储 Vue 节点形式 Mapping 数据，
            // 所以以遍历形式获取节点数据
            this.treeNode.forEach((ele) => {
                if (ele.item[this.nodeKey] == id) {
                    _node = ele
                    return false
                }
            })
            return _node
        },
        // 返回节点 Mapping 关系对象
        getNodes() {
            var mapping = {}
            for (let i = 0; i < this.treeNode.length; i++) {
                const element = this.treeNode[i]
                mapping[element.item[this.nodeKey]] = element
            }
            return mapping
        },
        // 根据 ID 值 为选中的数据赋值
        setCheckValueById(_array, val) {
            this.treeNode.forEach((ele) => {
                if (_array.includes(ele.item[this.nodeKey])) {
                    this.$nextTick(() => {
                        ele.changeTreeNodeValue(val)
                    })
                }
            })
        },
        // 获取选中的数据对象
        getCheckData() {
            var _node = []
            this.treeNode.forEach((ele) => {
                if (ele.checked) {
                    _node.push(ele.item)
                }
            })
            return _node
        }
    }
}
</script>

<style lang="less" scoped>
</style>
